/*
 * ShadowSpace.cc
 *
 * Author :
 *
 */
#include <cstdlib>
#include <string.h>

#include "../include/ShadowSpace.h"
#include "../include/util.h"
#include "../include/Iterator.h"

ShadowSpace::ShadowSpace() {
}

ShadowSpace::~ShadowSpace() {
    this->CleanSpace();
}

/**
 * Method used to reflect in records gotten by getRecords the changes made by
 * this transaction
 * 
 * @param recordsGotten Iterator returned by GetRecords
 * @return 
 */
ErrorCode ShadowSpace::UpdateIteratorList(Iterator ** recordsGotten) {
    try {
        this->readingIterators.push_back(recordsGotten);
        
        std::map<unsigned long, ModifiedRecord *>::iterator it_modRecords;
        it_modRecords = this->modifiedRecords.begin();

        // Searching for other values that might match
        // Table that has changes in DB records
        for (; it_modRecords != this->modifiedRecords.end(); it_modRecords++) {
            // If record in bounds
            if((*recordsGotten)->testBounds((*it_modRecords).second->valueInDb->key)){
                // If it was deleted
                if(it_modRecords->second->deleted){
                    // If it was also updated before being deleted
                    if(it_modRecords->second->updated)
                        (*recordsGotten)->deleteFromList(it_modRecords->second->newValue);
                    else
                        (*recordsGotten)->deleteFromList(it_modRecords->second->valueInDb);
                }
                else {
                    (*recordsGotten)->updateFromList(it_modRecords->second->newValue, it_modRecords->second->valueInDb);
                }
            }
        } // end for

        // Testing insert
        std::list< NewRecord*>::iterator it_addedRecords = this->addedRecords.begin();
        for (; it_addedRecords != this->addedRecords.end(); it_addedRecords++) {
            if ((*recordsGotten)->testBounds((*it_addedRecords)->value->key))
                (*recordsGotten)->insert((*it_addedRecords)->value);
        }
    } catch (std::bad_alloc &e) {
        return kErrorOutOfMemory;
    } catch (ErrorCode &e) {
        return e;
    } catch (...) {
        return kErrorGenericFailure;
    }
    return kOk;
}

/**
 * Insert this new record to the ShadowSpace
 * 
 * @param record
 * @return 
 */
ErrorCode ShadowSpace::InsertNewRecordList(Record *record) {
    try {
        this->addedRecords.push_back(new NewRecord(record));

        // Propagating new elements to all iterators that might contain that range
        std::list< Iterator**>::iterator it_iterators = this->readingIterators.begin();
        for (; it_iterators != this->readingIterators.end(); it_iterators++) { 
            if ((*(*it_iterators))->testBounds(record->key)) {
                Record * r = new Record();
                CopyRecord(r,record);
                
                (*(*it_iterators))->insert(record);
            }
        }
    } catch (std::bad_alloc &) {
        return kErrorOutOfMemory;
    } catch (...) {
        return kErrorGenericFailure;
    }
    return kOk;
}

/**
 * Update any new record present in the ShadowSpace that matches the query
 * 
 * @param record
 * @param new_payload
 * @param flags
 * @return 
 */
ErrorCode ShadowSpace::UpdateNewRecordList(Record *record, Block *new_payload, uint8_t flags) {
    bool found = false;
    
    try {
        bool updatePayload = false;

        std::list< NewRecord*>::iterator it_addedRecords = this->addedRecords.begin();
        for (; it_addedRecords != this->addedRecords.end(); it_addedRecords++) {
            if (keycmp(record->key, (*it_addedRecords)->value->key) == 0) {
                // Match payload
                if (flags < 2) {
                    if (BlockCmp((*it_addedRecords)->value->payload, *new_payload)) {
                        updatePayload = true;
                    }
                }// Match key
                else {
                    updatePayload = true;
                }
            }

            if (updatePayload == true) {
                updatePayload = false;
                found = true;

                try {
                    free((*it_addedRecords)->value->payload.data);
                } catch (...) {
                }

                (*it_addedRecords)->value->payload.size = new_payload->size;
                (*it_addedRecords)->value->payload.data = malloc((int) new_payload->size);
                memcpy((*it_addedRecords)->value->payload.data, new_payload->data,
                        (int) (*it_addedRecords)->value->payload.size);

                if ((flags == 0) || (flags == 2))
                    break;
            }
        }
    } catch (std::bad_alloc &) {
        throw (kErrorOutOfMemory);
    } catch (...) {
        throw (kErrorGenericFailure);
    }
    if (found)
        return kOk;
    else
        return kErrorNotFound;
}

/**
 * Method used to delete any new record present in the ShadowSpace that matches the query
 * 
 * @param record
 * @param flags
 * @return 
 */
ErrorCode ShadowSpace::DeleteNewRecordList(Record *record, uint8_t flags) {
    bool match = false;
    try {
        bool deleteRecord = false;
        
        // change flag in new list if present
        std::list< NewRecord*>::iterator it_addedRecords = this->addedRecords.begin();
        for (; it_addedRecords != this->addedRecords.end(); it_addedRecords++) {
            if (keycmp(record->key, (*it_addedRecords)->value->key) == 0) {
                // Match payload
                if (flags < 2) {
                    if (BlockCmp((*it_addedRecords)->value->payload, record->payload)) {
                        deleteRecord = true;
                    }
                }// Match key
                else {
                    deleteRecord = true;
                }
            }

            if (deleteRecord) {
                match = true;

                free((*it_addedRecords)->value->payload.data);
                (*it_addedRecords)->value->payload.size = 0;

                it_addedRecords = this->addedRecords.erase(it_addedRecords);

                if ((flags == 0) || (flags == 2))
                    break;
            }
        }
    } catch (std::bad_alloc &) {
        return kErrorOutOfMemory;
    } catch (...) {
        return kErrorGenericFailure;
    }
    if (match)
        return kOk;
    return kErrorNotFound;
}

/**
 * Method used to add update list to ShadowSpace
 * 
 * @param dbRecords
 * @param matchingRecord
 * @param newPayload
 * @param flags
 * @return 
 */
ErrorCode ShadowSpace::UpdateModifiedList(Iterator ** dbRecords, Record * matchingRecord, Block * newPayload, uint8_t flags) {
    bool found = false;
    try {
        std::map<unsigned long, ModifiedRecord *>::iterator it_modRecords;

        it_modRecords = this->modifiedRecords.begin();

        // Searching for other values that might match
        // Table that has changes in DB records
        for (; it_modRecords != this->modifiedRecords.end(); it_modRecords++) {
            // If deleted, don't check
            if (it_modRecords->second->deleted) {
                continue;
            }

            // If key match
            if (keycmp(it_modRecords->second->valueInDb->key, matchingRecord->key) == 0) {
                // Ignore payload
                if (flags > 1) {
                    CopyRecord(it_modRecords->second->newValue, matchingRecord->key, *newPayload);
                    found = true;
                }// Don't ignore payload
                else {
                    if (BlockCmp(it_modRecords->second->newValue->payload, matchingRecord->payload) == 0) {
                        CopyRecord(it_modRecords->second->newValue, matchingRecord->key, *newPayload);
                        found = true;
                    }
                }
            } // end if key matches

            // Found a record and looking only for one
            if (found && ((flags == 0) || (flags == 2)))
                return kOk;

        } // end for

        Record * record;
        // Adding records from the list
        while ((*dbRecords)->next()) {
            it_modRecords = this->modifiedRecords.find((unsigned long) (*dbRecords)->value());

            // If it doesn't exist in the map, add it
            if (it_modRecords == this->modifiedRecords.end()) {
                record = new Record();
                CopyRecord(record, matchingRecord->key, *newPayload);

                ModifiedRecord * modRecord = new ModifiedRecord((*dbRecords)->value(), record);

                this->modifiedRecords[(unsigned long) (*dbRecords)->value()] = modRecord;
                found = true;
            }// If it exists
            else {
                if (it_modRecords->second->deleted) {
                    continue;
                }

                // Ignore payload
                if (flags > 1) {
                    CopyRecord(it_modRecords->second->newValue, matchingRecord->key, *newPayload);
                    found = true;
                }// Don't ignore payload
                else {
                    if (BlockCmp(it_modRecords->second->newValue->payload, matchingRecord->payload) == 0) {
                        CopyRecord(it_modRecords->second->newValue, matchingRecord->key, *newPayload);
                        found = true;
                    }
                }
            }

            // Found a record and looking only for one
            if (found && ((flags == 0) || (flags == 2)))
                return kOk;
        }

        // If I don't find at least one record, return kErrorNotFound
        if (!found)
            return kErrorNotFound;

    } catch (std::bad_alloc &e) {
        return kErrorOutOfMemory;
    } catch (ErrorCode &e) {
        return e;
    } catch (...) {
        return kErrorGenericFailure;
    }
    return kOk;
}

/**
 * Method used to add delete list to ShadowSpace
 * 
 * @param dbRecords
 * @param matchingRecord
 * @param flags
 * @return 
 */
ErrorCode ShadowSpace::DeleteModifiedList(Iterator ** dbRecords, Record * matchingRecord, uint8_t flags) {
    bool found = false;
    try {
        std::map<unsigned long, ModifiedRecord *>::iterator it_modRecords;

        it_modRecords = this->modifiedRecords.begin();

        // Searching for other values that might match
        // Table that has changes in DB records
        for (; it_modRecords != this->modifiedRecords.end(); it_modRecords++) {
            // If deleted, don't check
            if (it_modRecords->second->deleted) {
                continue;
            }

            // If key match
            if (keycmp(it_modRecords->second->valueInDb->key, matchingRecord->key) == 0) {
                // Ignore payload
                if (flags > 1) {
                    it_modRecords->second->deleted = true;
                    found = true;
                }// Don't ignore payload
                else {
                    if (BlockCmp(it_modRecords->second->newValue->payload, matchingRecord->payload) == 0) {
                        it_modRecords->second->deleted = true;
                        found = true;
                    }
                }
            } // end if key matches

            // Found a record and looking only for one
            if (found && ((flags == 0) || (flags == 2)))
                return kOk;

        } // end for

        if (dbRecords == NULL) {
            // If I don't find at least one record, return kErrorNotFound
            if (!found)
                return kErrorNotFound;
        }

        // Adding records from the list
        while ((*dbRecords)->next()) {
            it_modRecords = this->modifiedRecords.find((unsigned long)(*dbRecords)->value());

            // If it doesn't exist in the map, add it
            if (it_modRecords == this->modifiedRecords.end()) {
                ModifiedRecord * modRecord = new ModifiedRecord((*dbRecords)->value());

                this->modifiedRecords[(unsigned long) (*dbRecords)->value()] = modRecord;
                found = true;
            }// If it exists
            else {
                if (it_modRecords->second->deleted) {
                    continue;
                }

                // Ignore payload
                if (flags > 1) {
                    it_modRecords->second->deleted = true;
                    found = true;
                }// Don't ignore payload
                else {
                    if (BlockCmp(it_modRecords->second->newValue->payload, matchingRecord->payload) == 0) {
                        it_modRecords->second->deleted = true;
                        found = true;
                    }
                }
            }

            // Found a record and looking only for one
            if (found && ((flags == 0) || (flags == 2)))
                return kOk;
        }

        // If I don't find at least one record, return kErrorNotFound
        if (!found)
            return kErrorNotFound;

    } catch (std::bad_alloc &e) {
        return kErrorOutOfMemory;
    } catch (ErrorCode &e) {
        return e;
    } catch (...) {
        return kErrorGenericFailure;
    }
    return kOk;
}

/**
 * Check if the record exists in the newRecord List and not on the deleted List
 * or if it is in the updatedList
 *
 * @return true if at least one copy of the record is present in the ShadowCopy
 *         this method does not check if the record was deleted
 */
bool ShadowSpace::checkExistance(Record *record, uint8_t flags) {
    try {
        std::list< NewRecord*>::iterator it_newRecords = this->addedRecords.begin();

        for (; it_newRecords != this->addedRecords.end(); it_newRecords++) {
            // Ignores payload
            if (flags > 1) {
                if (keycmp((*it_newRecords)->value->key, record->key) == 0) {
                    return true;
                }
            } else {
                if (recordCmp(*((*it_newRecords)->value), *record) == 0) {
                    return true;
                }
            }
        }
    } catch (...) {
    }
    return false;
}

/** 
 * Returns an Iterator with all the records present in addedRecords list
 */
ErrorCode ShadowSpace::getNewRecords(Iterator ** it) {
    if (it == NULL)
        return kErrorGenericFailure;

    try {
        (*it)->UnsetDeleteRecords();

        std::list< NewRecord*>::iterator it_newRecords = this->addedRecords.begin();
        for (; it_newRecords != this->addedRecords.end(); it_newRecords++) {
            (*it)->insert((*it_newRecords)->value);
        }
    } catch (std::bad_alloc &e) {
        return kErrorOutOfMemory;
    } catch (ErrorCode &e) {
        return e;
    } catch (...) {
        return kErrorGenericFailure;
    }
    return kOk;
}

/**
 * Returns an Iterator with all the records that should be deleted from db
 * 
 * @param 
 * @return 
 */
ErrorCode ShadowSpace::getRecordsToBeDeleted(Iterator ** it) {
    if (it == NULL)
        return kErrorGenericFailure;

    try {
        std::map<unsigned long, ModifiedRecord *>::iterator it_modRecords;

        it_modRecords = this->modifiedRecords.begin();

        for (; it_modRecords != this->modifiedRecords.end(); it_modRecords++) {
            if (it_modRecords->second->deleted) {
                (*it)->insert(it_modRecords->second->valueInDb);
            }
        }

    } catch (std::bad_alloc &e) {
        return kErrorOutOfMemory;
    } catch (ErrorCode &e) {
        return e;
    } catch (...) {
        return kErrorGenericFailure;
    }
    return kOk;
}

/**
 * Returns an Iterator with all the records that should be updated in db
 * 
 * @param 
 * @return 
 */
ErrorCode ShadowSpace::getRecordsToBeUpdated(std::list<ModifiedRecord*> ** recordsToBeUpdated) {
    if (recordsToBeUpdated == NULL)
        return kErrorGenericFailure;
    try {
        (*recordsToBeUpdated) = new std::list<ModifiedRecord*>();
        
        std::map<unsigned long, ModifiedRecord *>::iterator it_modRecords;

        it_modRecords = this->modifiedRecords.begin();

        for (; it_modRecords != this->modifiedRecords.end(); it_modRecords++) {
            if (it_modRecords->second->deleted){
                continue;
            }
            if (it_modRecords->second->updated) {
                (*recordsToBeUpdated)->push_back(it_modRecords->second);
            }
        }
    } catch (std::bad_alloc &e) {
        return kErrorOutOfMemory;
    } catch (ErrorCode &e) {
        return e;
    } catch (...) {
        return kErrorGenericFailure;
    }
    return kOk;
}

/**
 * Delete all the resources of the ShadowPage.
 * Called by the destructor, it might also be called from outside the class.
 */
void ShadowSpace::CleanSpace() {
    try {
        while (!this->addedRecords.empty()) {
            delete this->addedRecords.back();
            this->addedRecords.pop_back();
        }
    } catch (...) {
    }

    try {
        while (!this->readingIterators.empty()) {
            delete this->readingIterators.back();
            this->readingIterators.pop_back();
        }
    } catch (...) {
    }
}
